#include "MCCanvas.h"
#include "MCDrawCircleCommand.h"
#include "MCDrawEllipseCommand.h"
#include <MCUtils.h>
#include <MCAxis.h>
#include <iostream>
#include <qevent.h>
#include <MCWheelArgs.h>
#include <MCSegmentVisual.h>
#include <MCDrawLineSegmentCommand.h>
#include <MCKeyArgs.h>
#include <MCGripPoint.h>
#include <algorithm>
#include <stdio.h>
#include <MCDrawArcCommand.h>
#include <MCDrawLinearDimensionCommand.h>

MCCanvas::MCCanvas(QWidget *parent) :
    QGraphicsView(parent)
{
    setupScene();
    setupControllers();
}

int MCCanvas::width() const
{
    return _scene->width();
}

int MCCanvas::height() const
{
    return _scene->height();
}

double MCCanvas::scale()
{
    return _sceneScale;
}

const MCVector2 &MCCanvas::offset()
{
    return _sceneOffset;
}

void MCCanvas::addVisualItem(IGraphicsItem *item)
{
    if (!MCUtils::VectorContains(_visualItems, item))
    {
        _scene->addItem(static_cast<MCGraphicsItem *>(item));
        _visualItems.push_back(item);
    }

    redraw();
}

void MCCanvas::addCursorVisual(ICursorVisual *cursorVisual)
{
}

void MCCanvas::removeVisualItem(IGraphicsItem *item)
{
    if (MCUtils::VectorContains(_visualItems, item))
    {
        _scene->removeItem(static_cast<MCGraphicsItem *>(item));
        _visualItems.erase(std::remove(_visualItems.begin(), _visualItems.end(), item), _visualItems.end());
    }

    resetGrips();
    redraw();
}

IGraphicsItem *MCCanvas::itemAt(const MCPoint2D &position)
{
    for (size_t i = 0; i < _visualItems.size(); i++)
    {
        if (static_cast<MCGraphicsItem *>(_visualItems[i])->shape().contains(QPointF(position.x, position.y)))
        {
            return _visualItems[i];
        }
    }

    return nullptr;
}

IGripPoint *MCCanvas::gripAt(const MCPoint2D &position)
{
    for (size_t i = 0; i < _gripPoints.size(); i++)
    {
        if (static_cast<MCGripPoint *>(_gripPoints[i])->shape().contains(QPointF(position.x, position.y)))
        {
            if (static_cast<MCGripPoint *>(_gripPoints[i])->isVisible())
            {
                return _gripPoints[i];
            }
                
        }
    }

    return nullptr;
}

std::vector<IGraphicsItem *> MCCanvas::items()
{
    return _visualItems;
}

void MCCanvas::resetGrips()
{
    for (size_t i = 0; i < _gripPoints.size(); i++)
    {
        _scene->removeItem(static_cast<MCGripPoint *>(_gripPoints[i]));
    }

    _gripPoints.clear();

    for (size_t i = 0; i < _visualItems.size(); i++)
    {
        _visualItems[i]->resetGrips();

        for (size_t j = 0; j < _visualItems[i]->grips().size(); j++)
        {
            _gripPoints.push_back(_visualItems[i]->grips()[j]);
        }
    }
    
    for (size_t i = 0; i < _gripPoints.size(); i++)
    {
        _scene->addItem(static_cast<MCGripPoint *>(_gripPoints[i]));
    }
}

MCPoint2D MCCanvas::screenToWorld(const MCPoint2D &global)
{
    MCPoint2D result = global;
    result.y *= -1;
    result = result + _sceneOffset;
    result = result / _sceneScale; 

    return result;
}

MCPoint2D MCCanvas::worldToScreen(const MCPoint2D &local)
{
    MCPoint2D result = local;
    result = result * _sceneScale;
    result = result - _sceneOffset;
    result.y *= -1;

    return result;
}

void MCCanvas::setDrawingOffset(const MCVector2 &offset)
{
    _sceneOffset = _sceneOffset + offset;
}

void MCCanvas::setDrawingScale(double scale)
{
    _sceneScale *= scale;
}

void MCCanvas::redraw()
{
    _scene->update();
}

void MCCanvas::keyPressEvent(QKeyEvent *event)
{
    MCKeyArgs args(event, this);

    _commandManager->onKeyDown(args);
    _selectController->onKeyDown(args);
    _constraintManager->resolve();

    redraw();
}

void MCCanvas::mousePressEvent(QMouseEvent *event)
{
    MCMouseArgs args(event, this);

    _panController->onMouseDown(args);
    _commandManager->onMouseDown(args);
    _constraintManager->resolve();

    if (_commandManager->hasCommand() == false)
    {
        _selectController->onMouseDown(args);
        _gripController->onMouseDown(args);
    }

    redraw();
}

void MCCanvas::mouseReleaseEvent(QMouseEvent *event)
{
    MCMouseArgs args(event, this);

    _panController->onMouseUp(args);
    _commandManager->onMouseUp(args);
    _constraintManager->resolve();

    if (_commandManager->hasCommand() == false)
    {
        _selectController->onMouseUp(args);
        _gripController->onMouseUp(args);
    }

    redraw();
}

void MCCanvas::mouseMoveEvent(QMouseEvent *event)
{
    MCMouseArgs args(event, this);

    _panController->onMouseMove(args);
    _commandManager->onMouseMove(args);
    _constraintManager->resolve();

    if (_commandManager->hasCommand() == false)
    {
        _selectController->onMouseMove(args);
        _gripController->onMouseMove(args);
    }

    redraw();
}

void MCCanvas::wheelEvent(QWheelEvent *event)
{
    MCWheelArgs args(event, this);

    _zoomController->wheelDown(args);

    redraw();
}

MCConstraintsManager *MCCanvas::constraintManager()
{
    return _constraintManager;
}

MCCommandManager *MCCanvas::commandManager()
{
    return _commandManager;
}

MCSelectController *MCCanvas::selectController()
{
    return _selectController;
}

void MCCanvas::setupScene()
{
    setFocus();
    setMouseTracking(true);
    setRenderHint(QPainter::Antialiasing);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    _scene = new QGraphicsScene(this);
    setScene(_scene);
    QRectF rect(0, 0, 1280, 960);
    _scene->setSceneRect(rect);
    _scene->addItem(new MCAxis(this));

    _sceneScale = 1;
    _sceneOffset = MCVector2((width() / 2), (height() / 2));
}

void MCCanvas::setupControllers()
{
    _panController = new MCPanController(this);
    _zoomController = new MCZoomController(this);
    _commandManager = new MCCommandManager(this);
    _selectController = new MCSelectController(this);
    _gripController = new MCGripMover(this);
    _constraintManager = new MCConstraintsManager();
}
